package com.activenetwork.util.grid;

import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.events.EventFiringWebDriver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.activenetwork.custom.other.Checkbox;
import com.activenetwork.custom.other.XCheckbox;
import com.activenetwork.listener.ImplicitlyWaitListener;
import com.activenetwork.util.EnvContext;
import com.activenetwork.util.WaitUtil;
import com.activenetwork.util.WebDriverFactory;
import com.activenetwork.util.grid.condition.Condition;
import com.google.common.base.Optional;

public class DataGrid implements OperatableGrid, SortableGrid {
	
	public final WebDriver driver = EnvContext.webDriverTL.get();
	private Logger logger = LoggerFactory.getLogger(DataGrid.class);
	
	private By tbody;
	private By thead;
	private By pagination;
	private By filter;
	private By pageSize;
	
	public DataGrid(By bodyBy, By headBy, By paginationBy, By filterBy, By pageSizeBy){
		tbody = bodyBy;
		thead = headBy;
		pagination = paginationBy;
		filter = filterBy;
		pageSize = pageSizeBy;
	}
	
	private Optional<WebElement> getTable(By by){
		try{
			return Optional.of(driver.findElement(by));
		}catch(NoSuchElementException e){
			return Optional.absent();
		}
	}
	
	public Optional<WebElement> getBody(){
		return getTable(tbody);
	}
	public Optional<WebElement> getHead(){
		return getTable(thead);
	}
	public Optional<WebElement> getPagination(){
		return getTable(pagination);
	}
	public Optional<WebElement> getFilter(){
		return getTable(filter);
	}
	public Optional<WebElement> getPageSize(){
		return getTable(pageSize);
	}
	
	@Override
	public boolean isPagable(){
		List<WebElement> lis = getPagination().get().findElements(By.xpath(".//li"));
		if(lis.size() == 0){
			return false;
		}else{
			return true;
		}
	}
	
	@Override
	public boolean isFirstPage() {
		List<WebElement> lis = getPagination().get().findElements(By.xpath(".//li"));
		if(lis.size() == 0){
			return true;
		}
		
		WebElement previousPageLi = getPagination().get().findElement(By.xpath(".//li[1]"));
		if(previousPageLi.getAttribute("class").equals("disabled")){
			return true;
		}else{
			return false;
		}
	}
	
	@Override
	public boolean isLastPage() {
		List<WebElement> lis = getPagination().get().findElements(By.xpath(".//li"));
		if(lis.size() == 0){
			return true;
		}
		
		WebElement lastPageLi = getPagination().get().findElement(By.xpath(".//li[last()]"));
		if(lastPageLi.getAttribute("class").equals("disabled")){
			return true;
		}else{
			return false;
		}
	}
	
	@Override
	public void firstPage() {	
		if (!isFirstPage()) {
			WebElement firstPage = getPagination().get().findElement(By.xpath(".//li[2]//a"));
			firstPage.click();
			this.gridPagingProgress();
        }
	}
	
	@Override
	public void nextPage() {
		if(!isLastPage()){
			WebElement nextPage = getPagination().get().findElement(By.xpath(".//li[last()]//a"));
			nextPage.click();
			this.gridPagingProgress();
		}
	}
	
	@Override
	public void previousPage(){
		if(!isFirstPage()){
			WebElement previousPage = getPagination().get().findElement(By.xpath(".//li[1]//a"));
			previousPage.click();
			this.gridPagingProgress();
		}
		
	}
	
	@Override
	public void setGridSize(int size) {
		// not implemented yet
		// setup grid size
	}
	
	@Override
	public void checkOnRow(Condition... conditions) throws NoMatchRowException {
		int affectedRow = 0;
    	this.firstPage();
    	
    	while(true){
    		int rowNum = getBody().get().findElements(By.xpath("./tr[./td]")).size();
    		for(int i=1; i<=rowNum; i++){
    			WebElement tr = getBody().get().findElement(By.xpath("./tr[./td][" + i + "]"));
    			boolean flag = true;
 	            for (Condition condition : conditions) {
 	                flag = flag && condition.matchs(tr);
 	            }
 	            if (flag) {
 	            	new RowAction(){
 						@Override
 						public void act(WebElement row) {
 							Checkbox checkbox = new XCheckbox(row.findElement(By.xpath(".//input[@type='checkbox']")));
 							checkbox.checkON();
 						}
 	 	              }.act(tr);
 	 	            affectedRow++;
 	            }
    		}
    		if(this.isLastPage()){
				break;
			}else{
				this.nextPage();
			}
    	}
    	
    	if(affectedRow == 0){
    		throw new NoMatchRowException("Unable to check on any matched row");
    	}
    	
	}
	
	
	@Override
	public void expandRow(Condition... conditions) {
		// TODO Auto-generated method stub
		return ;
	}
	
	@Override
	public void clickRow(final int column, Condition... conditions) throws NoMatchRowException {
		this.firstPage();
		while(true){
    		int rowNum = getBody().get().findElements(By.xpath("./tr[./td]")).size();
    		for(int i=1; i<=rowNum; i++){
    			WebElement tr = getBody().get().findElement(By.xpath("./tr[./td][" + i + "]"));
    			boolean flag = true;
 	            for (Condition condition : conditions) {
 	                flag = flag && condition.matchs(tr);
 	            }
 	            if (flag) {
 	              new RowAction(){
					@Override
					public void act(WebElement row) {
						row.findElement(By.xpath(".//td[" + column + "]//a")).click();
					}
 	              }.act(tr);
 	              return;
 	            }
    		}
    		if(this.isLastPage()){
				break;
			}else{
				this.nextPage();
			}
    	}
		throw new NoMatchRowException("Unable to click any matached row link in sm grid");
	}
	

	
	@Override
	public void modifyRow(RowAction action, Condition... conditions) {
		int affectedRow = 0;
    	this.firstPage();
    	
    	while(true){
    		int rowNum = getBody().get().findElements(By.xpath("./tr[./td]")).size();
    		for(int i=1; i<=rowNum; i++){
    			WebElement tr = getBody().get().findElement(By.xpath("./tr[./td][" + i + "]"));
    			boolean flag = true;
 	            for (Condition condition : conditions) {
 	                flag = flag && condition.matchs(tr);
 	            }
 	            if (flag) {
 	                action.act(tr);
 	                affectedRow++;
 	            }
    		}
    		if(this.isLastPage()){
				break;
			}else{
				this.nextPage();
			}
    	}
    	
    	if(affectedRow == 0){
    		throw new NoMatchRowException("Unable to apply action on any matched row");
    	}
    	
	}

	@Override
	public void clickRowRadio(final int column, Condition... conditions) {
		this.firstPage();
		while(true){
    		int rowNum = getBody().get().findElements(By.xpath("./tr[./td]")).size();
    		for(int i=1; i<=rowNum; i++){
    			WebElement tr = getBody().get().findElement(By.xpath("./tr[./td][" + i + "]"));
    			boolean flag = true;
 	            for (Condition condition : conditions) {
 	                flag = flag && condition.matchs(tr);
 	            }
 	            if (flag) {
 	              new RowAction(){
					@Override
					public void act(WebElement row) {
						row.findElement(By.xpath(".//td[" + column + "]//input[@type='radio']")).click();
					}
 	              }.act(tr);
 	              return;
 	            }
    		}
    		if(this.isLastPage()){
				break;
			}else{
				this.nextPage();
			}
    	}
		throw new NoMatchRowException("Unable to click any matached row link in sm grid");
	}

	@Override
	public int countMatchRow(Condition... conditions){
		
		int affectedRow = 0;
    	this.firstPage();
    	
    	while(true){
    		int rowNum = getBody().get().findElements(By.xpath("./tr[./td]")).size();
    		for(int i=1; i<=rowNum; i++){
    			WebElement tr = getBody().get().findElement(By.xpath("./tr[./td][" + i + "]"));	
    			boolean flag = true;
 	            for (Condition condition : conditions) {
 	                flag = flag && condition.matchs(tr);
 	            }
 	            if (flag) {
 	 	            affectedRow++;
 	            }
    		}
    		if(this.isLastPage()){
				break;
			}else{
				this.nextPage();
			}
    	}
    	
    	// Go back 1st page
    	this.firstPage();
    	return affectedRow;
    	
	}
	
	@Override
	public WebElement getTd(int col, Condition... conditions) {
		this.firstPage();
		while(true){
    		int rowNum = getBody().get().findElements(By.xpath("./tr[./td]")).size();
    		for(int i=1; i<=rowNum; i++){
    			WebElement tr = getBody().get().findElement(By.xpath("./tr[./td][" + i + "]"));
    			boolean flag = true;
 	            for (Condition condition : conditions) {
 	                flag = flag && condition.matchs(tr);
 	            }
 	            if (flag) {
 	              return tr.findElement(By.xpath("./td[" + col + "]"));
 	            }
    		}
    		if(this.isLastPage()){
				break;
			}else{
				this.nextPage();
			}
    	}
		throw new NoMatchRowException("Unable to find the td cell matched the condition.");
	}

	@Override
	public WebElement getTd(int row, int col) {
		int rowNum = getBody().get().findElements(By.xpath("./tr[./td]")).size();
		if(row > rowNum){
			throw new NoMatchRowException("Table only has " + rowNum + " rows. You can not find the row:" + row);
		}
		
		WebElement tr = getBody().get().findElement(By.xpath("./tr[./td][" + row + "]"));
		List<WebElement> tds = tr.findElements(By.xpath("./td"));
		if(col > tds.size()){
			throw new NoMatchRowException("Table only has " + tds.size() + " columns. You can not find the column:" + col);
		}
		return tds.get(col - 1);
	}

	@Override
	public void checkAll() {
		WebElement element = getHead().get().findElement(By.xpath("./tr[./th][1]/th[1]//input[@type='checkbox']"));
		new XCheckbox(element).checkON();
	}

	@Override
	public void sort(int col, ColumnSort columnSort) throws UnsortableException{
		WebElement th = getHead().get().findElement(By.xpath("./tr/th[" + col + "]"));
		if(isHeadSortable(th)){
			WebElement icon = th.findElement(By.xpath(".//i"));
			String status = icon.getAttribute("class");
			while(!StringUtils.equals(status, columnSort.getOption())){
				icon.click();
				this.gridPagingProgress();
				th = getHead().get().findElement(By.xpath("./tr/th[" + col + "]"));
				icon = th.findElement(By.xpath(".//i"));
				status = icon.getAttribute("class");
			}
		}else{
			String thName = th.getText();
			throw new UnsortableException("Column " + col + " " + thName + " is not sortable");
		}
	}

	
	private boolean isHeadSortable(WebElement th){
		try{
			WebDriverFactory.unwrap((EventFiringWebDriver)driver, new ImplicitlyWaitListener());
			th.findElement(By.xpath(".//span[@class='nexus-sortable']"));
			return true;
		}catch(NoSuchElementException e){
			return false;
		}finally{
			WebDriverFactory.wrap((EventFiringWebDriver)driver, new ImplicitlyWaitListener());
		}
	}
	
	private void gridPagingProgress(){
		By progressBy = By.xpath("//div[@id='nexus-progress']");
		WaitUtil waitUtil = new WaitUtil();
		
		try{
			while(true){
				WebElement div = driver.findElement(progressBy);
				String attr = div.getAttribute("class");
				if(StringUtils.contains(attr, "hide")){
					break;
				}else{
					waitUtil.wait(500);
				}
			}
		}catch(NoSuchElementException e){
			logger.debug("Unable to find the nexus progress on this page ");
		}
	}
	
	
}
